/*
 * Created on 2005-3-12
 */
package com.hd123.rumba.user;

import java.util.Date;
import java.util.List;

import net.sf.hibernate.FlushMode;
import net.sf.hibernate.Hibernate;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.type.Type;

import com.hd123.rumba.domain.CDomain;
import com.hd123.rumba.domain.IDomain;
import com.hd123.rumba.domain.IDomainFuncView;
import com.hd123.rumba.exception.BusinessException;
import com.hd123.rumba.exception.DuplicateIdException;
import com.hd123.rumba.exception.ParameterException;
import com.hd123.rumba.log.EntityLogger;
import com.hd123.rumba.runtime.Manager;
import com.hd123.rumba.runtime.NamingMgr;
import com.hd123.rumba.runtime.OperateContext;

/**
 * @author caili
 */
public class RoleMgr extends Manager implements IRoleMgr {

  public static final String CAPTION_MANAGER = "角色管理员";

  private static final String EVENT_ASSIGNPERMISSION = "添加权限";
  private static final String EVENT_REMOVEPERMISSION = "删除权限";
  private static final String EVENT_SHARE = "共享";
  private static final String EVENT_UNSHARE = "取消共享";

  public RoleMgr() {
    super();
    this.registryClass("com.hd123.rumba.user.IRole", "com.hd123.rumba.user.Role");
    this.registryClass("com.hd123.rumba.user.IRolePermission",
        "com.hd123.rumba.user.RolePermission");
  }

  public IRole createRole(IDomain domain, String code, String name) throws HibernateException,
      BusinessException {
    checkParameterNotNull(domain, "domain");
    checkParameterNotNull(code, "code");
    checkParameterNotNull(name, "name");
    IRole role = getRoleByCode(domain, code);
    if (role != null)
      throw new DuplicateIdException("域" + domain.getCode() + "中已经存在code为" + code + "的角色");

    role = new Role();
    role.setDomain(domain);
    role.setCode(code);
    role.setName(name);
    role.setState(CRole.VALUE_STATE_NORMAL);
    role.setRoleType(CRole.VALUE_ROLETYPE_PRIVATE);
    return role;
  }

  public IRole createRole(IDomain domain) throws HibernateException, BusinessException {
    checkParameterNotNull(domain, "domain");
    IRole role = new Role();
    role.setDomain(domain);
    role.setState(CRole.VALUE_STATE_NORMAL);
    role.setRoleType(CRole.VALUE_ROLETYPE_PRIVATE);
    return role;
  }

  public IRole getRole(String uuid) throws BusinessException, HibernateException {
    return getRole(uuid, null);
  }

  public IRole getRole(String uuid, Integer[] states) throws BusinessException, HibernateException {
    if (uuid == null)
      return null;
    IRole role = (IRole) getSession().load(IRole.class, uuid);
    if (states == null)
      return role;
    for (int i = 0; i < states.length; i++)
      if (states[i] != null && states[i].equals(role.getState()))
        return role;
    return null;
  }

  public IRole getValidRole(String uuid) throws BusinessException, HibernateException {
    return getRole(uuid, new Integer[] {
      CRole.VALUE_STATE_NORMAL });
  }

  public IRole getRoleByCode(IDomain domain, String code) throws HibernateException,
      BusinessException {
    checkParameterNotNull(domain, "domain");
    IRole role = null;
    if (code != null) {
      List list = getSession().find(
          "from Role r where r.code = ? and r.domain.uuid=? and r.state <> ?", new Object[] {
              code.toUpperCase(), domain.getUuid(), CRole.VALUE_STATE_DELETED }, new Type[] {
              Hibernate.STRING, Hibernate.STRING, Hibernate.INTEGER });
      if (list.size() > 0) {
        role = (IRole) list.get(0);
      }
    }
    return role;
  }

  /** @deprecated */
  public void saveRole(IRole role) throws HibernateException, BusinessException {
    IUserMgr userMgr = (IUserMgr) NamingMgr.getInstance().lookupMgr(IUserMgr.class);
    saveRole(role, new OperateContext(userMgr.getSysUser()));
  }

  /** @deprecated */
  public void saveRole(IRole role, IUser operator, long oca) throws HibernateException,
      BusinessException {
    saveRole(role, new OperateContext(operator));
  }

  public void saveRole(IRole role, OperateContext operate) throws HibernateException,
      BusinessException {
    checkParameterNotNull(role, "role");
    checkParameterNotNull(operate, "operate");
    if (role.getUuid() == null || !CRole.VALUE_STATE_DELETED.equals(role.getState())) {
      getSession().setFlushMode(FlushMode.COMMIT);
      IDomain domain = role.getDomain();
      IRole erole = getRoleByCode(domain, role.getCode());
      if (erole != null && !erole.equals(role))
        throw new DuplicateIdException("域" + domain.getCode() + "中已经存在" + CRole.CAPTION_CODE + "为"
            + erole.getCode() + "的角色");
    }

    Date operTime = operate.getTime();
    EntityLogger logger = new EntityLogger(this);
    logger.setEntity(role);
    logger.setEntityConstClass(CRole.class);
    logger.setOperator(operate.getOperator());
    logger.setTime(operTime);

    boolean isNew = role.getUuid() == null;
    role.setLastModified(operTime);
    role.setLastModifier(operate.getOperator().getNameCode());
    getSession().saveOrUpdate(role);

    if (isNew) {
      IRoleDomain roleDomain = new RoleDomain();
      roleDomain.setDomain(role.getDomain());
      roleDomain.setRole(role);
      getSession().save(roleDomain);
    }

    if (isNew)
      logger.log(EntityLogger.EVENT_ADDNEW, role.getState(), CRole.CAPTION_CLASS + ":"
          + role.getNameCode());
    else
      logger.log(EntityLogger.EVENT_MODIFY, role.getState(), CRole.CAPTION_CLASS + ":"
          + role.getNameCode());
  }

  public void removeRole(IRole role, OperateContext operate) throws HibernateException,
      BusinessException {
    checkParameterNotNull(role, "role");
    checkParameterNotNull(operate, "operate");
    EntityLogger logger = new EntityLogger(this);
    logger.setEntity(role);
    logger.setEntityConstClass(CRole.class);
    logger.setOperator(operate.getOperator());
    logger.setTime(operate.getTime());
    logger.log(EntityLogger.EVENT_REMOVE, role.getState(), role.getNameCode());
    getSession().delete("from RolePermission rp where rp.role.uuid=?", role.getUuid(),
        Hibernate.STRING);
    getSession().delete("from UserRole ur where ur.role.uuid=?", role.getUuid(), Hibernate.STRING);
    getSession().delete(role);
  }

  public void deleteRole(IRole role, OperateContext operate) throws HibernateException,
      BusinessException {
    checkParameterNotNull(role, "role");
    checkParameterNotNull(operate, "operate");

    Date operTime = operate.getTime();
    EntityLogger logger = new EntityLogger(this);
    logger.setEntity(role);
    logger.setEntityConstClass(CRole.class);
    logger.setOperator(operate.getOperator());
    logger.setTime(operTime);
    logger.log("删除角色", CRole.VALUE_STATE_DELETED, CRole.CAPTION_CLASS + ":" + role.getNameCode());

    role.setState(CRole.VALUE_STATE_DELETED);
    role.setLastModified(operTime);
    role.setLastModifier(operate.getOperator().getNameCode());
    getSession().saveOrUpdate(role);

    getSession()
        .delete("from UserRole ur where ur.role.uuid = ?", role.getUuid(), Hibernate.STRING);
  }

  public void undeleteRole(IRole role, OperateContext operate) throws HibernateException,
      BusinessException {
    checkParameterNotNull(role, "role");
    checkParameterNotNull(operate, "operate");
    if (CRole.VALUE_STATE_DELETED.equals(role.getState())) {
      getSession().setFlushMode(FlushMode.COMMIT);
      IDomain domain = role.getDomain();
      IRole erole = getRoleByCode(domain, role.getCode());
      if (erole != null)
        throw new DuplicateIdException("域" + domain.getNameCode() + "中已经存在" + CRole.CAPTION_CODE
            + "为\"" + erole.getCode() + "\"的角色。");
    }

    Date operTime = operate.getTime();
    EntityLogger logger = new EntityLogger(this);
    logger.setEntity(role);
    logger.setEntityConstClass(CRole.class);
    logger.setOperator(operate.getOperator());
    logger.setTime(operTime);
    logger.log("恢复已删除角色", CRole.VALUE_STATE_NORMAL, CRole.CAPTION_CLASS + ":" + role.getNameCode());

    role.setState(CRole.VALUE_STATE_NORMAL);
    role.setLastModified(operTime);
    role.setLastModifier(operate.getOperator().getNameCode());
    getSession().saveOrUpdate(role);
  }

  /** @deprecated */
  public void assignPermissionToRole(IPermission perm, IRole role) throws HibernateException,
      BusinessException {
    IUserMgr userMgr = (IUserMgr) NamingMgr.getInstance().lookupMgr(IUserMgr.class);
    assignPermissionToRole(perm, role, new OperateContext(userMgr.getSysUser()));
  }

  public void assignPermissionToRole(IPermission perm, IRole role, OperateContext operate)
      throws HibernateException, BusinessException {
    checkParameterNotNull(perm, "perm");
    checkParameterNotNull(role, "role");
    checkParameterNotNull(operate, "operate");
    if (!hasPermission(role, perm)) {
      IRolePermission rp = new RolePermission();
      rp.setPermission(perm);
      rp.setRole(role);
      getSession().save(rp);

      EntityLogger logger = new EntityLogger(this);
      logger.setEntity(role);
      logger.setEntityConstClass(IRole.class);
      logger.setOperator(operate.getOperator());
      logger.setTime(operate.getTime());
      logger.log(EVENT_ASSIGNPERMISSION, role.getState(), CPermission.CAPTION_CLASS + ":"
          + perm.getCode());
    }
  }

  public boolean hasPermission(IRole role, IPermission perm) throws HibernateException,
      BusinessException {
    checkParameterNotNull(perm, "perm");
    checkParameterNotNull(role, "role");
    List list = getSession().find(
        "from RolePermission rp where rp.role.uuid=? and rp.permission.uuid=?", new Object[] {
            role.getUuid(), perm.getUuid() }, new Type[] {
            Hibernate.STRING, Hibernate.STRING });
    return list.size() > 0;
  }

  /** @deprecated */
  public void removePermissionFromRole(IPermission perm, IRole role) throws HibernateException,
      BusinessException {
    IUserMgr userMgr = (IUserMgr) NamingMgr.getInstance().lookupMgr(IUserMgr.class);
    removePermissionFromRole(perm, role, new OperateContext(userMgr.getSysUser()));
  }

  public void removePermissionFromRole(IPermission perm, IRole role, OperateContext operate)
      throws HibernateException, BusinessException {
    checkParameterNotNull(perm, "perm");
    checkParameterNotNull(role, "role");
    checkParameterNotNull(operate, "operate");
    getSession().delete(
        "from RolePermission as rp where rp.permission.uuid = ? and rp.role.uuid = ?",
        new Object[] {
            perm.getUuid(), role.getUuid() }, new Type[] {
            Hibernate.STRING, Hibernate.STRING });

    EntityLogger logger = new EntityLogger(this);
    logger.setEntity(role);
    logger.setEntityConstClass(IRole.class);
    logger.setOperator(operate.getOperator());
    logger.setTime(operate.getTime());
    logger.log(EVENT_REMOVEPERMISSION, role.getState(), CPermission.CAPTION_CLASS + ":"
        + perm.getCode());
  }

  /**
   * @param role
   * @return List of IPermission
   * @throws HibernateException
   */
  public List createPermissionsList(IRole role) throws HibernateException, BusinessException {
    this.checkParameterNotNull(role, "role");
    return getSession().find(
        "select distinct p from " + RolePermission.class.getName()
            + " rp inner join rp.permission p, " + IDomainFuncView.class.getName() + " dfv, "
            + IRoleDomain.class.getName() + " pr "
            + " where dfv.funcView.uuid = p.cartFuncViewUuid"
            + "  and dfv.domain.uuid = pr.domain.uuid and pr.role.uuid = rp.role.uuid "
            + " and rp.role.uuid = ? order by p.code", role.getUuid(), Hibernate.STRING);
  }

  /**
   * @param role
   * @return List of IUser
   * @throws HibernateException
   */
  public List createUsersList(IRole role) throws HibernateException, BusinessException {
    return getSession().find(
        "select u from User as u, UserRole as ur "
            + " where u.uuid = ur.user.uuid and ur.role.uuid = ? and u.state <> ?"
            + " order by u.login", new Object[] {
            role.getUuid(), CUser.VALUE_STATE_DELETED }, new Type[] {
            Hibernate.STRING, Hibernate.INTEGER });
  }

  /**
   * @param domain
   * @return List of IRole
   * @throws HibernateException
   * @throws ParameterException
   */
  public List createAllRolesList(IDomain domain) throws HibernateException, BusinessException {
    if (domain == null) {
      throw new ParameterException("必须的参数domain不能为null");
    }
    return getSession().find(
        "select pr.role from " + IRoleDomain.class.getName()
            + " pr where pr.domain.uuid = ? order by pr.role.code", domain.getUuid(),
        Hibernate.STRING);
  }

  public List createUsersListByRoleAndDomain(IRole role, IDomain domain) throws HibernateException,
      BusinessException {
    return getSession()
        .find(
            "select u from "
                + " User as u, UserRole as ur "
                + " where u.uuid=ur.user.uuid and ur.role.uuid =? and u.domain.uuid = ? order by u.login",
            new Object[] {
                role.getUuid(), domain.getUuid() }, new Type[] {
                Hibernate.STRING, Hibernate.STRING });
  }

  public void shareRoleToDomain(IRole role, IDomain domain, OperateContext operate)
      throws HibernateException, BusinessException {
    this.checkParameterNotNull(role, "role");
    this.checkParameterNotNull(domain, "domain");
    this.checkParameterNotNull(operate, "operate");

    if (!CRole.VALUE_ROLETYPE_PUBLIC.equals(role.getRoleType()))
      throw new BusinessException("指定的" + CRole.CAPTION_CLASS + "(" + role.getNameCode() + ")不是"
          + CRole.CAPTION_ROLETYPE_PUBLIC + "，不能共享。");
    if (CRole.VALUE_STATE_DELETED.equals(role.getState()))
      throw new BusinessException("指定的" + CRole.CAPTION_CLASS + "(" + role.getNameCode()
          + ")已经被删除，不能共享。");

    if (!hasSharedToDomain(role, domain)) {
      IRoleDomain rd = new RoleDomain();
      rd.setRole(role);
      rd.setDomain(domain);
      getSession().save(rd);

      EntityLogger logger = new EntityLogger(this);
      logger.setEntity(role);
      logger.setEntityConstClass(IRole.class);
      logger.setOperator(operate.getOperator());
      logger.setTime(operate.getTime());
      logger.log(EVENT_SHARE, role.getState(), CDomain.CAPTION_CLASS + ":" + domain.getNameCode());
    }
  }

  protected boolean hasSharedToDomain(IRole role, IDomain domain) throws BusinessException,
      HibernateException {
    List list = getSession().find(
        "from " + IRoleDomain.class.getName() + " o where o.role.uuid = ? and o.domain.uuid = ?",
        new Object[] {
            role.getUuid(), domain.getUuid() }, new Type[] {
            Hibernate.STRING, Hibernate.STRING });
    return list.size() > 0;
  }

  public void unshareRoleFromDomain(IRole role, IDomain domain, OperateContext operate)
      throws HibernateException, BusinessException {
    this.checkParameterNotNull(role, "role");
    this.checkParameterNotNull(domain, "domain");
    this.checkParameterNotNull(operate, "operate");

    if (!CRole.VALUE_ROLETYPE_PUBLIC.equals(role.getRoleType()))
      throw new BusinessException("指定的" + CRole.CAPTION_CLASS + "(" + role.getNameCode() + ")不是"
          + CRole.CAPTION_ROLETYPE_PUBLIC + "，不能取消共享。");
    if (CRole.VALUE_STATE_DELETED.equals(role.getState()))
      throw new BusinessException("指定的" + CRole.CAPTION_CLASS + "(" + role.getNameCode()
          + ")已经被删除，不能取消共享。");

    getSession().delete(
        "from " + IRoleDomain.class.getName() + " o where o.role.uuid = ? and o.domain.uuid = ?",
        new Object[] {
            role.getUuid(), domain.getUuid() }, new Type[] {
            Hibernate.STRING, Hibernate.STRING });

    getSession().delete(
        "from " + IUserRole.class.getName()
            + " ur where ur.user.domain.uuid = ? and ur.role.uuid = ?", new Object[] {
            domain.getUuid(), role.getUuid() }, new Type[] {
            Hibernate.STRING, Hibernate.STRING });

    EntityLogger logger = new EntityLogger(this);
    logger.setEntity(role);
    logger.setEntityConstClass(IRole.class);
    logger.setOperator(operate.getOperator());
    logger.setTime(operate.getTime());
    logger.log(EVENT_UNSHARE, role.getState(), CDomain.CAPTION_CLASS + ":" + domain.getNameCode());
  }

  public List<IDomain> createRoleShareDomains(IRole role) throws BusinessException,
      HibernateException {
    this.checkParameterNotNull(role, "role");
    List list = getSession().find(
        "select rs.domain from " + IRoleDomain.class.getName()
            + " rs where rs.role.uuid = ? order by rs.domain.code", role.getUuid(),
        Hibernate.STRING);
    return list;
  }
}
